package problem;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.GregorianCalendar;

import algorithms.concBT.ConcBT10CPAAgentMaker;
import algorithms.concBT.ConcBT1CPAAgentMaker;
import algorithms.concBT.ConcBTAgentMaker;
import algorithms.concCBJ.ConcCBJAgentMaker;
import algorithms.disCBJ.DisCBJAgentMaker;

import ordering.VariableOrdering;
import ordering.dyn.RandomOrdering;
import ordering.stat.ConflictOrdering;
import ordering.stat.ConstraintOrdering;
import ordering.stat.DefaultOrdering;
import ordering.stat.OpConflictOrdering;
import ordering.stat.OpConstraintOrdering;

import priorities.EqualPriority;
import priorities.MiddleFirst;
import priorities.MostSendingFirst;
import priorities.MostSendingLast;
import priorities.PriorityManager;


import general.TwoLongs;
import general.CPA.ArrayCPA;
import general.CPA.CPA;
import general.CPA.DynamicCPA;
import general.constraints.ArrayConstraints;
import general.constraints.Constraints;

public class Problem {

	//	private static final int DBT = 1;
	//	private static final int DBT_FC = 2;
	//	private static final int DEFAULT_ORDERING = 3;
	//	private static final int MINDOMAIN_ORDERING = 4;
	//	private static final int CONSTRAINT_ORDERING = 5;
	private static final int DEFAULT_SPLIT_LIMIT = 20;
	private static final int DEFAULT_DELAY = 0;
	private static final CPA DEFAULT_CPA = new ArrayCPA();
	private static final PriorityManager DEFAULT_PRIORITY = new EqualPriority();
	private static VariableOrdering DEFAULT_ORDERING;

	private static final int numProblems = 100;

	private byte n;
	private byte domain;
	private float p1;
	private float p2;

	public Problem(byte n, byte domain, float p1, float p2){
		this.n = n;
		this.domain = domain;
		this.p1 = p1;
		this.p2 = p2;
	}

	public Constraints makeConstraints(){
		Constraints constraints = new ArrayConstraints(n, domain);
		int numConstraints = 0;
		int numConflicts = 0;
		for (byte i=0; i<n; i++){
			for (byte j=(byte)(i+1); j<n; j++){
				double rand1 = Math.random();
				if (rand1 < p1){	//there is a constraint between i and j
					numConstraints++;
					for (byte k=0; k<domain; k++){
						for (byte l=0; l<domain; l++){
							double rand2 = Math.random();
							if (rand2 < p2){	//there is a conflict between 2 values
								numConflicts++;
								constraints.addConstraint(i, j, k, l);
								constraints.addConstraint(j, i, l, k);
							}
						}
					}
				}
			}
		}
		DEFAULT_ORDERING = new DefaultOrdering(n); // TODO move this to MAIN method
		return constraints;
	}

	public static Constraints nQueensConstraints(byte n){
		Constraints constraints = new ArrayConstraints(n, n);
		int numConstraints = 0;
		int numConflicts = 0;
		for (byte i=0; i<n; i++){
			for (byte j=(byte)(i+1); j<n; j++){
				numConstraints++;
				for (byte k=0; k<n; k++){
					for (byte l=0; l<n; l++){
						if (k==l || i+k==j+l || i-k==j-l){
							numConflicts++;
							constraints.addConstraint(i, j, k, l);
							constraints.addConstraint(j, i, l, k);
						}
					}
				}
			}
		}
		return constraints;
	}

	static PrintWriter genlogger2 = null;
	static{
		File f1 = new File("timesLOG.txt");
		f1.delete();
		try {
			f1.createNewFile();
			genlogger2 = new PrintWriter(f1);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private static void log(String line){
		genlogger2.println(line);
		genlogger2.flush();
		System.out.println(line);
	}

	//Ordering test
	public static void test1(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test1_ConcBT_NCCCs", "test1_ConcBT_MSGs", "test1_DisCBJ_NCCCs", "test1_DisCBJ_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": Ordering test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in Ordering test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);
				DisCBJAgentMaker discbjam= new DisCBJAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[10];
				String[] res = new String[10];
				String[] lines = new String[]{"","","",""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new DefaultOrdering(n), new ConstraintOrdering(n, c), new ConflictOrdering(n, c), new OpConstraintOrdering(n, c), new OpConflictOrdering(n, c), 
						new DefaultOrdering(n), new ConstraintOrdering(n, c), new ConflictOrdering(n, c), new OpConstraintOrdering(n, c), new OpConflictOrdering(n, c)};
				for (int k=0; k<5; k++){
					results[k] = cbtam.solve(c, orderings[k], DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);
					results[k+5] = discbjam.solve(c, orderings[k+5], DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY);
					res[k+5] = discbjam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished DisCBJ, #" + k +" iteration " + "\t" + results[k+5].long1 + "\t" + results[k+5].long2 + "\t" + res[k+5]);

					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
					lines[2] = lines[2] + "\t" + results[k+5].long1;
					lines[3] = lines[3] + "\t" + results[k+5].long2;
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//test scheduling policies
	public static void test2(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test2_NCCCs", "test2_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": Scheduling test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in Scheduling test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[4];
				String[] res = new String[4];
				String[] lines = new String[]{"",""};
				PriorityManager[] pms = new PriorityManager[]{
						new EqualPriority(), new MiddleFirst(n), new MostSendingFirst(n), new MostSendingLast(n)};
				for (int k=0; k<4; k++){
					results[k] = cbtam.solve(c, DEFAULT_ORDERING, DEFAULT_CPA, DEFAULT_DELAY, pms[k], DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);
					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
				}

				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//test delays
	//	public static void test3(byte n, byte d, float p1){
	//		//		String[] fileNames = 
	//		//			new String[]{"ConcBT_Test3_0", "ConcBT_Test3_20", "ConcBT_Test3_40", "ConcBT_Test3_60", "ConcBT_Test3_80", "ConcBT_Test3_100",   
	//		//				"DisCBJ_Test3_0", "DisCBJ_Test3_20", "DisCBJ_Test3_40", "DisCBJ_Test3_60", "DisCBJ_Test3_80", "DisCBJ_Test3_100"};
	//		String[] fileNames = new String[]{"ConcBT_test3_NCCCs", "ConcBT_test3_MSGs", "DisCBJ_test3_NCCCs", "DisCBJ_test3_MSGs"};
	//		PrintWriter[] loggers = initLoggers(fileNames, p1);
	//		log(GregorianCalendar.getInstance().getTime().toString() + ": Message delays test started");
	//		for (double i=0.0; i<9.0; i=i+1){
	//			for (int j = 0; j<numProblems ; j++){
	//				long time = GregorianCalendar.getInstance().getTimeInMillis();
	//				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+i+","+j + " in Message delays test");
	//				float p2 = (float)i*0.1f + 0.1f;
	//				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
	//				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);
	//				DisCBJAgentMaker discbjam= new DisCBJAgentMaker(n, d);
	//
	//				TwoLongs[] results = new TwoLongs[12];
	//				String[] res = new String[12];
	//				String[] lines = new String[]{"","","",""};
	//				int[] delays = new int[]{0, 20, 40, 60, 80, 100};
	//				for (int k=0; k<6; k++){
	//					results[k] = cbtam.solve(c, DEFAULT_ORDERING, DEFAULT_CPA, delays[k], DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
	//					res[k] = cbtam.getSolution();
	//					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);
	//					results[k+6] = discbjam.solve(c, DEFAULT_ORDERING, DEFAULT_CPA, delays[k], DEFAULT_PRIORITY);
	//					res[k+6] = cbtam.getSolution();
	//					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished DisCBJ, #" + k +" iteration " + "\t" + results[k+6].long1 + "\t" + results[k+6].long2 + "\t" + res[k+6]);
	//
	//					lines[0] = lines[0] + "\t" + results[k].long1;
	//					lines[1] = lines[1] + "\t" + results[k].long2;
	//					lines[2] = lines[2] + "\t" + results[k+6].long1;
	//					lines[3] = lines[3] + "\t" + results[k+6].long2;
	//				}
	//				checkSolution(res);
	//				printResultsToLoggers(loggers, lines, p2);
	//				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+i + ","+j+ " in Scheduling test");
	//				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
	//			}
	//		}
	//		closeLoggers(loggers);
	//	}

	//test split limits
	public static void test3(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test3_NCCCs", "test3_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": Split limits test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				System.gc();
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in Split limits test - p2=" + p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[5];
				String[] res = new String[results.length];
				String[] lines = new String[]{"",""};
				int[] splitLimits= new int[]{5, 10, 15, 20, 40};
				for (int k=0; k<results.length; k++){
					results[k] = cbtam.solve(c, DEFAULT_ORDERING, DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY, splitLimits[k]);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);

					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+i + ","+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}


	//Ordering distribution test
	public static void test4(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test4_NCCCs", "test4_MSGs", "test4_distNCCCs", "test4_distMsgs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": Ordering distribution test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in Ordering distribution test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[3];
				String[] res = new String[3];
				String[] lines = new String[]{"","","",""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new DefaultOrdering(n), new ConflictOrdering(n, c), new RandomOrdering((byte)0)};
				CPA[] cpas = new CPA[]{
						new ArrayCPA(), new ArrayCPA(), new DynamicCPA()};
				for (int k=0; k<3; k++){
					results[k] = cbtam.solve(c, orderings[k], cpas[k], DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);

					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
					lines[2] = lines[2] + "\t" + cbtam.getDistNCCCs();
					lines[3] = lines[3] + "\t" + cbtam.getDistMsgs();
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//Dynamic cpa test
	public static void test5(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test5_NCCCs", "test5_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": Dynamic CPA test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in Dynamic CPA test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[2];
				String[] res = new String[2];
				String[] lines = new String[]{"", ""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new ConflictOrdering(n, c), new ConflictOrdering(n, c)};
				CPA[] cpas = new CPA[]{ new ArrayCPA(), new DynamicCPA()};
				for (int k=0; k<2; k++){
					results[k] = cbtam.solve(c, orderings[k], cpas[k], DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);
					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//concurrency test
	public static void test6(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test6_NCCCs", "test6_MSGs", "test6_conc"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": concurrency test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in concurrency test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[2];
				String[] res = new String[2];
				String[] lines = new String[]{"", "", ""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new ConflictOrdering(n, c), new RandomOrdering((byte)0)};
				CPA[] cpas = new CPA[]{new DynamicCPA(), new DynamicCPA()};
				for (int k=0; k<2; k++){
					results[k] = cbtam.solve(c, orderings[k], cpas[k], DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);
					lines[0] = lines[0] + "\t" + results[k].long1;
					lines[1] = lines[1] + "\t" + results[k].long2;
					lines[2] = lines[2] + "\t" + cbtam.getConcurrency();
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//concCBJ test
	public static void test7(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test7_NCCCs", "test7_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": concCBJ test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in concCBJ test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);
				ConcCBJAgentMaker ccbjam = new ConcCBJAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[4];
				String[] res = new String[4];
				String[] lines = new String[]{"", ""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new ConflictOrdering(n, c), new RandomOrdering((byte)0),
						new ConflictOrdering(n, c), new RandomOrdering((byte)0)};
				CPA[] cpas = new CPA[]{ 
						new ArrayCPA(), new DynamicCPA(), 
						new ArrayCPA(), new DynamicCPA()};
				for (int k=0; k<2; k++){
					results[k] = cbtam.solve(c, orderings[k], cpas[k], DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k] = cbtam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished DisCBJ, #" + k +" iteration " + "\t" + results[k].long1 + "\t" + results[k].long2 + "\t" + res[k]);

					results[k+2] = ccbjam.solve(c, orderings[k+2], cpas[k+2], DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
					res[k+2] = ccbjam.getSolution();
					log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcCBJ, #" + k +" iteration " + "\t" + results[k+2].long1 + "\t" + results[k+2].long2 + "\t" + res[k+2]);

					lines[0] = lines[0] + "\t" + results[k].long1 + "\t" + results[k+2].long1;
					lines[1] = lines[1] + "\t" + results[k].long2 + "\t" + results[k+2].long2;
				}
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	//concurrency2 test
	public static void test8(byte n, byte d, float p1){
		String[] fileNames = new String[]{"test8_NCCCs", "test8_MSGs"};
		PrintWriter[] loggers = initLoggers(fileNames, p1);
		log(GregorianCalendar.getInstance().getTime().toString() + ": concurrency2 test started");
		for (double i=0.0; i<9.0; i=i+1){
			for (int j = 0; j<numProblems ; j++){
				long time = GregorianCalendar.getInstance().getTimeInMillis();
				float p2 = (float)i*0.1f + 0.1f;
				log (GregorianCalendar.getInstance().getTime().toString() + ": Started round number "+j + " in concurrency2 test, p2="+p2);
				Constraints c = new Problem(n,d,p1,p2).makeConstraints();
				ConcBTAgentMaker cbtam = new ConcBTAgentMaker(n, d);
				ConcBT1CPAAgentMaker cbtam2 = new ConcBT1CPAAgentMaker(n, d);
				ConcBT10CPAAgentMaker cbtam3 = new ConcBT10CPAAgentMaker(n, d);

				TwoLongs[] results = new TwoLongs[3];
				String[] res = new String[3];
				String[] lines = new String[]{"", ""};
				VariableOrdering[] orderings = new VariableOrdering[]{
						new ConflictOrdering(n, c), 
						new ConflictOrdering(n, c), 
						new ConflictOrdering(n, c)};
				results[0] = cbtam.solve(c, orderings[0], DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY, DEFAULT_SPLIT_LIMIT);
				res[0] = cbtam.getSolution();
				log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT, #" + 0 +" iteration " + "\t" + results[0].long1 + "\t" + results[0].long2 + "\t" + res[0]);

				results[1] = cbtam2.solve(c, orderings[1], DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY, Integer.MAX_VALUE);
				res[1] = cbtam2.getSolution();
				log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT1CPA, #" + 0 +" iteration " + "\t" + results[1].long1 + "\t" + results[1].long2 + "\t" + res[1]);

				results[2] = cbtam3.solve(c, orderings[2], DEFAULT_CPA, DEFAULT_DELAY, DEFAULT_PRIORITY, Integer.MAX_VALUE);
				res[2] = cbtam3.getSolution();
				log("	" + GregorianCalendar.getInstance().getTime().toString() + ": Finished ConcBT10CPA, #" + 0 +" iteration " + "\t" + results[2].long1 + "\t" + results[2].long2 + "\t" + res[2]);

				lines[0] = lines[0] + "\t" + results[0].long1 + "\t" + results[1].long1 + "\t" + results[2].long1;
				lines[1] = lines[1] + "\t" + results[0].long2 + "\t" + results[1].long2 + "\t" + results[2].long2;
				checkSolution(res);
				printResultsToLoggers(loggers, lines, p2);
				log (GregorianCalendar.getInstance().getTime().toString() + ": Finished round number "+j+ " in Scheduling test");
				log (GregorianCalendar.getInstance().getTime().toString() + ": for p2=" + p2 + " , Round lasted " + ((GregorianCalendar.getInstance().getTimeInMillis()-time)) + " milliseconds");
			}
		}
		closeLoggers(loggers);
	}

	private static void closeLoggers(PrintWriter[] loggers){
		for (int i=0; i<loggers.length; i++){
			loggers[i].close();
		}
	}

	private static PrintWriter[] initLoggers(String[] fileNames, float p1){
		File[] files = new File[fileNames.length];
		PrintWriter[] loggers = new PrintWriter[files.length];
		for (int i=0; i<files.length; i++){
			files[i] = new File(fileNames[i]+"-"+p1+".txt");
			files[i].delete();
			try {
				files[i].createNewFile();
				loggers[i] = new PrintWriter(files[i]);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return loggers;
	}

	//	private static void printResultsToLoggers(PrintWriter[] loggers, TwoLongs[] results, float p2){
	//		for (int k=0; k<results.length; k++){
	//			loggers[k].println(p2 + "\t" + results[k].long1 + "\t" + results[k].long2);
	//			loggers[k].flush();
	//			System.out.println(p2 + "\t" + results[k].long1 + "\t" + results[k].long2);
	//		}
	//	}

	private static void printResultsToLoggers(PrintWriter[] loggers, String[] line, float p2){
		for (int k=0; k<loggers.length; k++){
			loggers[k].println(p2 + line[k]);
			loggers[k].flush();
			System.out.println(p2 + line[k]);
		}
	}

	private static void checkSolution(String[] res){
		boolean hasSolution = false;
		boolean hasNoSolution = false;
		for (int i=0; i<res.length; i++){
			if (res[i]!=null) hasSolution = true;
			if (res[i]==null) hasNoSolution = true;
		}
		if (hasSolution && hasNoSolution){
			System.out.println("Resolving");
			log("ERRORRRRRRRRRRRRRRRRRRRRR");
			for (int i=0; i<res.length; i++){
				log(res[i]);
			}
		}
	}

	public static void main(String[] args){

		long time = GregorianCalendar.getInstance().getTimeInMillis();
		//		
		//				byte numQueens = 15;
		//				Constraints p = nQueensConstraints(numQueens);
		//				
		//				ConcCBJAgentMaker b1 = new ConcCBJAgentMaker(numQueens, numQueens);
		//				ConcBTAgentMaker b2 = new ConcBTAgentMaker(numQueens, numQueens);
		//				
		//				System.out.println(b1.solve(p, new ConstraintOrdering(numQueens, p), new ArrayCPA(), 0, new EqualPriority(), 15));
		//				System.out.println(b2.solve(p, new ConstraintOrdering(numQueens, p), new ArrayCPA(), 0, new EqualPriority(), 15));
		//				System.out.println(b1.getSolution());
		//				System.out.println(b2.getSolution());
		byte n = 10;
		byte d = 8;

		System.out.println("Started test 1");
		test1(n, d, 0.4f);
		test1(n, d, 0.8f);
		long time1 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 2");
		test2(n, d, 0.4f);
		test2(n, d, 0.8f);
		long time2 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 3");
		test3(n, d, 0.4f);
		test3(n, d, 0.8f);
		long time3 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 4");
		test4(n, d, 0.4f);
		test4(n, d, 0.8f);
		long time4 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 5");
		test5(n, d, 0.4f);
		test5(n, d, 0.8f);
		long time5 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 6");
		test6(n, d, 0.4f);
		test6(n, d, 0.8f);
		long time6 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 7");
		test7(n, d, 0.4f);
		test7(n, d, 0.8f);
		long time7 = GregorianCalendar.getInstance().getTimeInMillis();
		System.out.println("Started test 8");
		test8(n, d, 0.4f);
		test8(n, d, 0.8f);
		long time8 = GregorianCalendar.getInstance().getTimeInMillis();
		long tmptime = time8 - time;
		System.out.println("Run lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time1 - time;
		System.out.println("Test1 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time2 - time1;
		System.out.println("Test2 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time3 - time2;
		System.out.println("Test3 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time4 - time3;
		System.out.println("Test4 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time5 - time4;
		System.out.println("Test5 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time6 - time5;
		System.out.println("Test6 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time7 - time6;
		System.out.println("Test7 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
		tmptime = time8 - time7;
		System.out.println("Test8 lasted " + tmptime + " milliseconds , or ~" + (tmptime/1000) + " seconds, or ~" + (tmptime/1000/60) + " minutes, or ~" + (tmptime/1000/60/60) + " hours, HAVE FUN!!");
	}
}
//problems to generate:		(to begin with)
//4 scheduling policies experiments for each of the problems with a single ordering heuristics, 0 delay (8 problems)
//3 different ordering heuristics for each of the algorithms with a single scheduling policy, 0 delay (6 problems)
//5 message delays with a single ordering heuristics and a single scheduling policy (10 problems)


//ordering heuristics
//most constrainted first
//most conflicted first
//least constrainted
//default ordering
//random
//order agents by distance between them
//enforce arc consistency + min domain

//i assumed that we cant implement policies that rely on other variables constraints - since they are private

//thread scheduling 
//more messages - less CPU
//static ordering - middle agents gets bigger priority, agents on the bounds gets lower priority
//everyone same priority

//message delays
//0 miliseconds
//20 milisecnods
//40 miliseconds
//60 miliseconds
//80 miliseconds
//100 miliseconds

//testing split limit for ConcBT
//15
//35
//55
//75
//95

//measureing
//NCCCs
//number of sent messages

//number of splits performed
//number of unique SP's that each process knows
//maximum number of unique active SPs in each agent (we can check the number in each agent to measure how good we distribute the time)

//measurement to concurrency - number of active SPs - can draw a graph of the number of active SPs for each agent
//TODO MAKE SURE TO REMOVE OTHER FEATURES WHILE TESTING A SPECIFIC FEATURE TO MAKE THINGS MORE EFFICIENT